package com.q3d.demo.common.lib.service.response;

import java.io.IOException;
import java.lang.reflect.Type;

import org.springframework.core.NamedThreadLocal;

import com.alibaba.fastjson2.JSONObject;
import com.q3d.demo.common.lib.service.exception.ServiceException;

import feign.FeignException;
import feign.Response;
import feign.Util;
import feign.codec.DecodeException;
import feign.codec.Decoder;

/**
 * 专门为 FeignClient 解析泛型响应结构体的 Decoder
 * 
 * @author 叶湘 ( weixin:yexiang841, email:yexiang841@qq.com )
 */
public class ResponseFeignDecoder implements Decoder {

    /**
     * 线程级缓存，用于在每个请求前写入转换类型
     */
    @SuppressWarnings(value = "rawtypes") /** 允许使用 Class 粗类型 */
    private static NamedThreadLocal<Class> responseFeignDecoderThreadLocal = new NamedThreadLocal<Class>(
            "responseFeignDecoderThreadLocal");

    /**
     * 调用 Feign 的泛型接口前，先调用 GenericsFeignResultDecoder.setReturnType()方法设置接口返回类型
     * 每个请求都是独立线程，从 NamedThreadLocal 中获取的 dtoType 不会跟别的请求相关，因此没有线程安全问题
     */
    @SuppressWarnings(value = "rawtypes") /** 允许使用 Class 粗类型 */
    public static void setReturnType(Class dtoType) {
        responseFeignDecoderThreadLocal.set(dtoType);
    }

    /**
     * 重写Decode
     *
     * @param response the response to decode
     * @param type     {@link java.lang.reflect.Method#getGenericReturnType()
     *                 generic return type} of the
     *                 method corresponding to this {@code response}.
     * @return instance of {@code type}
     * @throws IOException     will be propagated safely to the caller.
     * @throws DecodeException when decoding failed due to a checked exception
     *                         besides IOException.
     * @throws FeignException  when decoding succeeds, but conveys the operation
     *                         failed.
     */
    @Override
    @SuppressWarnings(value = { "rawtypes", "unchecked" }) /** 允许使用 未知 类型 */
    public Object decode(Response response, Type type) throws IOException, DecodeException, FeignException {
        try {
            // 若 response 为空，说明响应结构体有问题
            if (response == null) {
                responseFeignDecoderThreadLocal.remove();
                throw new ServiceException(ResponseStatus.INTERNAL_SERVER_ERROR, "Response_Got_Null");
            }
            // 从线程缓存里获取解码类型
            Class dtoType = responseFeignDecoderThreadLocal.get();
            String bodyStr = Util.toString(response.body().asReader(Util.UTF_8));
            JSONObject responseBody = JSONObject.parseObject(bodyStr);
            // 根据 ResponseBase 的结构自动映射响应结构体
            Object object = ResponseUtil.parseResponseBody(dtoType, responseBody);
            // 结构化返回
            return ResponseBase.success(object);
        } finally {
            // 尽量清掉不必要的残留数据
            responseFeignDecoderThreadLocal.remove();
        }
    }

}
